/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/markup/MarkupArchive.h>
#include <pgl/igfx/SVGInterface.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

namespace gfxinterface
{

// SVGWriter.cpp: implementation of the CSVGInterface class.
//
//////////////////////////////////////////////////////////////////////
#pragma warning(disable: 4786)  // identifier was truncated to 'number' characters in the debug information
#include <iostream>
#include <strstream>
#include <math.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


CSVGInterface::CSVGInterface()
: CFileGfxInterface(),m_eOutputType(OutputSVG)
{
}

CSVGInterface::~CSVGInterface()
{
}

void CSVGInterface::InitState()
{
	memset(&m_state.m_cStroke,0,sizeof(SRGBA));
	memset(&m_state.m_cSolidFill,1,sizeof(SRGBA));
	memset(&m_state.m_cShadow,0,sizeof(SRGBA));
	m_state.m_cShadow.a=0.2;
	m_state.m_cStroke.a=1;
	m_state.m_sDash=_T("");
	m_state.m_dLinewidth=1;
	GetDefaultLOGFONT(m_state.m_font);

	m_state.m_eFillType = SState::SolidFill;
	m_state.m_sLinearFill.m_cStart=SRGBA(0,0,0,1);
	m_state.m_sLinearFill.m_cEnd=SRGBA(1,1,1,1);
	m_state.m_sLinearFill.m_pStart=SPointD(0,0);
	m_state.m_sLinearFill.m_pEnd=SPointD(1,1);

	m_liCurDef=1;

	m_lStates.empty();
	m_xml.SetDoc(NULL);
}


void CSVGInterface::BeginPaint(HDC hDC)
{
	using namespace markup;

	CFileGfxInterface::BeginPaint(hDC);

	ASSERT(m_lStates.empty());

	InitState();
	WriteHeader();

	m_xmlDefs.AddElem(_T("defs"));

}

void CSVGInterface::EndPaint()
{
	CFileGfxInterface::EndPaint();
	ASSERT(m_lStates.empty());

	// adding definitions to m_xml...
	m_xml.ResetPos();
	if (m_xml.FindElem(_T("svg")) )
		m_xml.InsertChildSubDoc( m_xmlDefs.GetDoc() );

	ClearStream();
	m_stream<<(const TCHAR*)m_xml.GetDoc();
}

void CSVGInterface::DrawRect(double llx, double lly, double urx, double ury, DWORD dwRectFlag, SPointD sShadowSize)
{
	CString str, filterID;
	double wllx=WorldToBBx(llx);
	double wlly=WorldToBBy(ury);
	double wulx=WorldToBBAbsx(urx-llx);
	double wuly=WorldToBBAbsy(ury-lly);

	// lower left
	if (dwRectFlag & RectTypeShadowLowerRight
		|| dwRectFlag & RectTypeShadowLowerLeft
		|| dwRectFlag & RectTypeShadowLowerRight
		|| dwRectFlag & RectTypeShadowUpperLeft
		|| dwRectFlag & RectTypeShadowUpperRight
		|| dwRectFlag & RectTypeSoftShadowLowerLeft
		|| dwRectFlag & RectTypeSoftShadowLowerRight
		|| dwRectFlag & RectTypeSoftShadowUpperLeft
		|| dwRectFlag & RectTypeSoftShadowUpperRight)
	{
		double rx,ry;

		// computing offset y
		if (dwRectFlag & RectTypeShadowLowerLeft 
			|| dwRectFlag & RectTypeShadowLowerRight
			|| dwRectFlag & RectTypeSoftShadowLowerLeft
			|| dwRectFlag & RectTypeSoftShadowLowerRight)
		{
			ry=sShadowSize.y;
		}
		else
			ry=-sShadowSize.y;

		// computing offset x
		if (dwRectFlag & RectTypeShadowLowerLeft 
			|| dwRectFlag & RectTypeShadowUpperLeft
			|| dwRectFlag & RectTypeSoftShadowLowerLeft
			|| dwRectFlag & RectTypeSoftShadowUpperLeft)
		{
			rx=-sShadowSize.x;
		}
		else
			rx=sShadowSize.x;


		// creating filter...
		m_xmlDefs.AddChildElemEx(_T("filter"));
		filterID.Format(_T("filter%d"),m_liCurDef);
		m_xmlDefs.AddChildAttribEx(_T("id"),filterID);
		filterID.Format(_T("url(#filter%d)"),m_liCurDef);
	
		m_liCurDef++;

	
		m_xmlDefs.IntoElem();
			//offset filter
			m_xmlDefs.AddChildElemEx(_T("feOffset"));
			m_xmlDefs.AddChildAttribEx(_T("in"), _T("SourceGraphic"));
			m_xmlDefs.AddChildAttribEx(_T("dx"), rx);
			m_xmlDefs.AddChildAttribEx(_T("dy"), ry);
			m_xmlDefs.AddChildAttribEx(_T("result"), _T("ShadowAlpha"));
			// flood
			m_xmlDefs.AddChildSubDoc(_T("<feFlood in=\"ShadowAlpha\" flood-color=\"FillPaint\" flood-opacity=\"0.4\" result=\"ShadowFlood\"/>\r\n"));
			// composite
			m_xmlDefs.AddChildSubDoc(_T("<feComposite in=\"ShadowFlood\" in2=\"ShadowAlpha\" operator=\"in\" result=\"Shadow\"/>\r\n"));
			// apply bluring if solft shadow
			if (dwRectFlag & RectTypeSoftShadowLowerLeft
				|| dwRectFlag & RectTypeSoftShadowLowerRight
				|| dwRectFlag & RectTypeSoftShadowUpperLeft
				|| dwRectFlag & RectTypeSoftShadowUpperRight)
				m_xmlDefs.AddChildSubDoc(_T("<feGaussianBlur in=\"Shadow\" stdDeviation=\"4\" result=\"Shadow\"/>\r\n"));

			// merge
			m_xmlDefs.AddChildSubDoc(_T("<feMerge><feMergeNode in=\"Shadow\"/><feMergeNode in=\"SourceGraphic\"/></feMerge>\r\n"));			//merge
		m_xmlDefs.OutOfElem();
	}

/*	RectTypeShadowLowerLeft =		0x0004,
	RectTypeShadowLowerRight =		0x0008,
	RectTypeShadowUpperLeft =		0x0010,
	RectTypeShadowUpperRight =		0x0020,
	RectTypeSoftShadowLowerLeft =	0x0040,
	RectTypeSoftShadowLowerRight =	0x0080,
	RectTypeSoftShadowUpperLeft =	0x0100,
	RectTypeSoftShadowUpperRight =	0x0200	
*/


	// adding rect...
	m_xml.AddChildElemEx(_T("rect"));

	if (!filterID.IsEmpty())
		m_xml.AddChildAttribEx(_T("filter"), filterID);

	AddFillColorAttrib(m_xml, dwRectFlag & RectTypeFilled);
	AddStrokeColorAttrib(m_xml, !(dwRectFlag & RectTypeNoContour));

	m_xml.AddChildAttribEx(_T("x"),wllx);
	m_xml.AddChildAttribEx(_T("y"),wlly);
	m_xml.AddChildAttribEx(_T("width"),wulx);
	m_xml.AddChildAttribEx(_T("height"),wuly);

}

void CSVGInterface::DrawArrow(double tailx, double taily, double tipx, double tipy, double tailthickness, double  headthickness, double headlength, bool filled)
{
	
}

void CSVGInterface::DrawArrowAbs(double tailx, double taily, double dtipx, double dtipy, double tailthickness, double  headthickness, double headlength, bool filled)
{

}

void CSVGInterface::DrawLine(double x1, double y1, double x2, double y2)
{
	m_xml.AddChildElemEx(_T("line"));
	AddStrokeColorAttrib(m_xml,true);
	
	m_xml.AddChildAttribEx(_T("x1"),WorldToBBx(x1));
	m_xml.AddChildAttribEx(_T("y1"),WorldToBBy(y1));
	m_xml.AddChildAttribEx(_T("x2"),WorldToBBx(x2));
	m_xml.AddChildAttribEx(_T("y2"),WorldToBBy(y2));
}

void CSVGInterface::DrawArc(double cnx, double cny, double rn, double angle, double aperture, double rep, DWORD flags)
{
	if ( rn==0 || aperture == 0)
		return;

	m_xml.AddChildElemEx(_T("path"));
	AddFillColorAttrib(m_xml, (flags & ArcTypeFill)!=0);
	AddStrokeColorAttrib(m_xml,flags & ArcTypeContour);

	float cx=(float)(GetBBClipLeft() + cnx*GetBBClipWidth());
	float cy=(float)(GetBBClipTop() + cny*GetBBClipHeight());
	float radius = (float)__min(rn*GetBBClipWidth(),rn*GetBBClipHeight());
	
	angle=NormalizeAngle(angle);
	aperture=NormalizeAngle(aperture);
	double anglerad=-IGFX_PI*angle/180.0;
	double aperturerad=-IGFX_PI*aperture/180.0;

	// moving if exploded
	if (flags & ArcTypeExploded)
	{
		cx+=(float)(rep*cos(anglerad+aperturerad/2.0));
		cy+=(float)(rep*sin(anglerad+aperturerad/2.0));
	}

	// creating path...
	CString d;
	
	d.Format(_T("M%g,%g  L%g,%g A%g,%g %g 0,0 %g,%g Z"),
		cx,cy, // M
		cx+radius*cos(anglerad),cy+radius*sin(anglerad), // L
		radius,radius,
		angle,
		cx+radius*cos(anglerad+aperturerad), cy+radius*sin(anglerad+aperturerad)
		);

	m_xml.AddChildAttribEx(_T("d"),d);
}

void CSVGInterface::DrawBandStrip(const CVectorF& vX, const CVectorF& vYBottom, const CVectorF& vYTop, DWORD eBandTypeFlag, double dTension)
{

}

void CSVGInterface::DrawDonnut(double cnx, double cny, double rin, double rout, double angle, double aperture, double rep, DWORD flags)
{
	if ( rout==0 || rin == rout || aperture == 0)
		return;

	m_xml.AddChildElemEx(_T("path"));
	AddFillColorAttrib(m_xml, (flags & ArcTypeFill)!=0);

	AddStrokeColorAttrib(m_xml, !(flags & ArcTypeContour));

	float cx=(float)(GetBBClipLeft() + cnx*GetBBClipWidth());
	float cy=(float)(GetBBClipTop() + cny*GetBBClipHeight());
	float radiusIn = (float)__min(rin*GetBBClipWidth(),rin*GetBBClipHeight());
	float radiusOut = (float)__min(rout*GetBBClipWidth(),rout*GetBBClipHeight());
	
	angle=NormalizeAngle(angle);
	aperture=NormalizeAngle(aperture);
	double anglerad=-IGFX_PI*angle/180.0;
	double aperturerad=-IGFX_PI*aperture/180.0;

	// moving if exploded
	if (flags & ArcTypeExploded)
	{
		cx+=(float)(rep*cos(anglerad+aperturerad/2.0));
		cy+=(float)(rep*sin(anglerad+aperturerad/2.0));
	}

	// creating path...
	CString d;

	
	
	d.Format(_T("M%g,%g L%g,%g A%g,%g %g 0,1 %g,%g L%g,%g A%g,%g %g 0,0 %g,%g Z"),			
		cx+radiusOut*cos(anglerad+aperturerad), cy+radiusOut*sin(anglerad+aperturerad), // M
		cx+radiusIn*cos(anglerad+aperturerad),cy+radiusIn*sin(anglerad+aperturerad), // L
		radiusIn, radiusIn, //A
		-angle,
		cx+radiusIn*cos(anglerad),cy+radiusIn*sin(anglerad),
		cx+radiusOut*cos(anglerad), cy+radiusOut*sin(anglerad), // L
		radiusOut,radiusOut, //A
		angle,
		cx+radiusOut*cos(anglerad+aperturerad), cy+radiusOut*sin(anglerad+aperturerad)
		);

	m_xml.AddChildAttribEx(_T("d"),d);
}

void CSVGInterface::DrawLineStrip(const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag, double dTension)
{
	UINT n = __min(vX.size(),vY.size());
	CString str,strTemp,filterID;

	if (n<2)
		return;

	if (eLineTypeFlag & LineTypeShadow)
	{

		// creating filter...
		m_xmlDefs.AddChildElemEx(_T("filter"));
		filterID.Format(_T("filter%d"),m_liCurDef);
		m_xmlDefs.AddChildAttribEx(_T("id"),filterID);
		filterID.Format(_T("url(#filter%d)"),m_liCurDef);
		
		m_liCurDef++;

		m_xmlDefs.IntoElem();
			//offset filter
			m_xmlDefs.AddChildElemEx(_T("feOffset"));
			m_xmlDefs.AddChildAttribEx(_T("in"), _T("SourceGraphic"));
			m_xmlDefs.AddChildAttribEx(_T("dx"), m_state.m_dLinewidth);
			m_xmlDefs.AddChildAttribEx(_T("dy"), m_state.m_dLinewidth);
			m_xmlDefs.AddChildAttribEx(_T("result"), _T("ShadowAlpha"));
			// flood
			m_xmlDefs.AddChildSubDoc(_T("<feFlood in=\"ShadowAlpha\" flood-color=\"FillPaint\" flood-opacity=\"0.4\" result=\"ShadowFlood\"/>\r\n"));
			// composite
			m_xmlDefs.AddChildSubDoc(_T("<feComposite in=\"ShadowFlood\" in2=\"ShadowAlpha\" operator=\"in\" result=\"Shadow\"/>\r\n"));
			// merge
			m_xmlDefs.AddChildSubDoc(_T("<feMerge><feMergeNode in=\"Shadow\"/><feMergeNode in=\"SourceGraphic\"/></feMerge>\r\n"));			//merge
		m_xmlDefs.OutOfElem();
	}

	if (dTension == 0 || 1)
	{
		// open or closed
		if (eLineTypeFlag & LineTypeClosed)
			m_xml.AddChildElemEx(_T("polygon"));
		else
			m_xml.AddChildElemEx(_T("polyline"));
	
	
		str=_T("");

		if (eLineTypeFlag & LineTypeSnapBottom)
		{
			strTemp.Format(_T("%g,%g "),WorldToBBx(vX[0]),WorldToBBy(BBToWorldy(GetBBBottom())) );
			str+=strTemp;
		}

		for (UINT i=0;i<n;i++)
		{
			if (i%5 == 0)	
				str+=_T("\n");
			strTemp.Format(_T("%g,%g "),WorldToBBx(vX[i]),WorldToBBy(vY[i]));
			str+=strTemp;
		}
	
		if (eLineTypeFlag & LineTypeSnapBottom)
		{
			strTemp.Format(_T("%g,%g "),WorldToBBx(vX[n-1]),WorldToBBy(BBToWorldy(GetBBBottom())) );
			str+=strTemp;
		}
		m_xml.AddChildAttribEx(_T("points"),str);
	}
	else
	{
		double wx,wx1,wy,wy1;
		m_xml.AddChildElemEx(_T("path"));

		str=_T("");
		wx1=WorldToBBx(vX[0]);
		wy1=WorldToBBy(vY[0]);
		strTemp.Format(_T("M%g,%g "),wx1,wy1);
		str+=strTemp;
		for (UINT i=1;i<n;i++)
		{
			if (i%5 == 0)	
				str+=_T("\n");

			wx=wx1;
			wx1=WorldToBBx(vX[i]);
			wy=wy1;
			wy1=WorldToBBy(vY[i]);
			strTemp.Format(_T("S%g,%g %g,%g "),wx1,wy1+10*dTension,wx1,wy1);
			str+=strTemp;
		}

		if (eLineTypeFlag & LineTypeClosed)
			str+=_T("Z");

		m_xml.AddChildAttribEx(_T("d"),str);
	}

	// adding filling options
	AddFillColorAttrib(m_xml, (eLineTypeFlag & LineTypeFilled) != 0 );
	AddStrokeColorAttrib(m_xml,!(eLineTypeFlag & LineTypeNoContour));
	if (!filterID.IsEmpty())
		m_xml.AddChildAttribEx(_T("filter"),filterID);
}

void CSVGInterface::DrawMultipleLineStrip(int ns, const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag)
{
	int n = __min(vX.size(),vY.size());
	CString str,strTemp,filterID;	
	int index, nscur;

	if (n<2)
		return;

	if (eLineTypeFlag & LineTypeShadow)
	{

		// creating filter...
		m_xmlDefs.AddChildElemEx(_T("filter"));
		filterID.Format(_T("filter%d"),m_liCurDef);
		m_xmlDefs.AddChildAttribEx(_T("id"),filterID);
		filterID.Format(_T("url(#filter%d)"),m_liCurDef);
		
		m_liCurDef++;

		m_xmlDefs.IntoElem();
			//offset filter
			m_xmlDefs.AddChildElemEx(_T("feOffset"));
			m_xmlDefs.AddChildAttribEx(_T("in"), _T("SourceGraphic"));
			m_xmlDefs.AddChildAttribEx(_T("dx"), m_state.m_dLinewidth);
			m_xmlDefs.AddChildAttribEx(_T("dy"), m_state.m_dLinewidth);
			m_xmlDefs.AddChildAttribEx(_T("result"), _T("ShadowAlpha"));
			// flood
			m_xmlDefs.AddChildSubDoc(_T("<feFlood in=\"ShadowAlpha\" flood-color=\"FillPaint\" flood-opacity=\"0.4\" result=\"ShadowFlood\"/>\r\n"));
			// composite
			m_xmlDefs.AddChildSubDoc(_T("<feComposite in=\"ShadowFlood\" in2=\"ShadowAlpha\" operator=\"in\" result=\"Shadow\"/>\r\n"));
			// merge
			m_xmlDefs.AddChildSubDoc(_T("<feMerge><feMergeNode in=\"Shadow\"/><feMergeNode in=\"SourceGraphic\"/></feMerge>\r\n"));			//merge
		m_xmlDefs.OutOfElem();
	}

	m_xml.AddChildElemEx(_T("path"));

	index=0;
	nscur=__max(0,__min( ns, n-index));
	while (nscur)
	{	
		strTemp.Format(_T("M%g,%g L"),WorldToBBx(vX[index]),WorldToBBy(vY[index]));
		str+=strTemp;
		for (int i=index+1;i<index+nscur;i++)
		{
			if (i%10 == 0)	
				str+=_T("\n");
			strTemp.Format(_T("%g,%g "),WorldToBBx(vX[i]), WorldToBBy(vY[i]));
			str+=strTemp;
		}
		index+=nscur;
		nscur=__max(0,__min( ns, n-index));
	}

	if (eLineTypeFlag & LineTypeClosed  && n>2)
		str+=_T("Z");

	m_xml.AddChildAttribEx(_T("d"),str);

	// adding filling options
	AddFillColorAttrib(m_xml, (eLineTypeFlag & LineTypeFilled) != 0 );
	AddStrokeColorAttrib(m_xml,!(eLineTypeFlag & LineTypeNoContour));
	if (!filterID.IsEmpty())
		m_xml.AddChildAttribEx(_T("filter"),filterID);
}


void CSVGInterface::DrawStepStrip(const CVectorF& vX, const CVectorF& vY, DWORD eLineTypeFlag)
{
	UINT n = __min(vX.size(),vY.size());
	CString str,strTemp,filterID;
	double x1,x,y;

	if (n<2)
		return;

	if (eLineTypeFlag & LineTypeShadow)
	{

		// creating filter...
		m_xmlDefs.AddChildElemEx(_T("filter"));
		filterID.Format(_T("filter%d"),m_liCurDef);
		m_xmlDefs.AddChildAttribEx(_T("id"),filterID);
		filterID.Format(_T("url(#filter%d)"),m_liCurDef);
		
		m_liCurDef++;

		m_xmlDefs.IntoElem();
			//offset filter
			m_xmlDefs.AddChildElemEx(_T("feOffset"));
			m_xmlDefs.AddChildAttribEx(_T("in"), _T("SourceGraphic"));
			m_xmlDefs.AddChildAttribEx(_T("dx"), m_state.m_dLinewidth);
			m_xmlDefs.AddChildAttribEx(_T("dy"), m_state.m_dLinewidth);
			m_xmlDefs.AddChildAttribEx(_T("result"), _T("ShadowAlpha"));
			// flood
			m_xmlDefs.AddChildSubDoc(_T("<feFlood in=\"ShadowAlpha\" flood-color=\"FillPaint\" flood-opacity=\"0.4\" result=\"ShadowFlood\"/>\r\n"));
			// composite
			m_xmlDefs.AddChildSubDoc(_T("<feComposite in=\"ShadowFlood\" in2=\"ShadowAlpha\" operator=\"in\" result=\"Shadow\"/>\r\n"));
			// merge
			m_xmlDefs.AddChildSubDoc(_T("<feMerge><feMergeNode in=\"Shadow\"/><feMergeNode in=\"SourceGraphic\"/></feMerge>\r\n"));			//merge
		m_xmlDefs.OutOfElem();
	}

	// open or closed
	if (eLineTypeFlag & LineTypeClosed  && n>2)
		m_xml.AddChildElemEx(_T("polygon"));
	else
		m_xml.AddChildElemEx(_T("polyline"));

	AddFillColorAttrib( m_xml, (eLineTypeFlag & LineTypeFilled) != 0);
	AddStrokeColorAttrib(m_xml,!(eLineTypeFlag & LineTypeNoContour));
	if (!filterID.IsEmpty())
		m_xml.AddChildAttribEx(_T("filter"),filterID);

	str=_T("");
	if (eLineTypeFlag & LineTypeSnapBottom)
	{
		strTemp.Format(_T("%g,%g "),WorldToBBx(vX[0]),WorldToBBy(BBToWorldy(GetBBBottom())) );
		str+=strTemp;
	}

	x1=WorldToBBx(vX[0]);
	for (UINT i=0;i<n-1;i++)
	{
		if (i%5 == 0)	
			str+=_T("\n");

		x=x1;
		x1=WorldToBBx(vX[i+1]);
		y=WorldToBBy(vY[i]);
		strTemp.Format(_T("%g,%g %g,%g "),x,y,x1,y);
		str+=strTemp;
	}

	if (eLineTypeFlag & LineTypeSnapBottom)
	{
		strTemp.Format(_T("%g,%g "),WorldToBBx(vX[n-1]),WorldToBBy(BBToWorldy(GetBBBottom())) );
		str+=strTemp;
	}

	m_xml.AddChildAttribEx(_T("points"),str);
}

void CSVGInterface::DrawCircle(double cx, double cy, double radius, DWORD eLineType)
{
	m_xml.AddChildElemEx(_T("circle"));
	AddFillColorAttrib(m_xml,(eLineType & LineTypeFilled) != 0);
	AddStrokeColorAttrib(m_xml, !(eLineType & LineTypeNoContour));

	m_xml.AddChildAttribEx(_T("cx"),WorldToBBx(cx));
	m_xml.AddChildAttribEx(_T("cy"),WorldToBBy(cy));
	m_xml.AddChildAttribEx(_T("r"),radius);
}

void CSVGInterface::DrawCircleStrip(const CVectorF& vCx, const CVectorF& vCy, double radius, DWORD eLineType )
{
	CString str;
	const UINT n = __min(vCx.size(), vCy.size());
	if (n==0)
		return;

	m_xmlDefs.AddChildElemEx(_T("circle"));
	str.Format(_T("circ%d"),m_liCurDef);
	m_xmlDefs.AddChildAttribEx(_T("id"),str);
	m_xmlDefs.AddChildAttribEx(_T("r"),radius);
	AddFillColorAttrib(m_xmlDefs,(eLineType & LineTypeFilled) != 0);
	AddStrokeColorAttrib(m_xmlDefs, !(eLineType & LineTypeNoContour));
	
	str.Format(_T("#circ%d"),m_liCurDef);
	for (UINT i=0;i<n;i++)
	{
		m_xml.AddChildElemEx(_T("use"));
		m_xml.AddChildAttribEx(_T("x"),WorldToBBx(vCx[i]));
		m_xml.AddChildAttribEx(_T("y"),WorldToBBy(vCy[i]));
		m_xml.AddChildAttribEx(_T("xlink:href"),str);
	}

	m_liCurDef++;
}

void CSVGInterface::DrawPolygon(double cx, double cy,  const CVectorF& vPx, const CVectorF& vPy, DWORD eLineType)
{
	const UINT m = __min(vPx.size(), vPy.size());
	if (m==0)
		return;
	UINT i;

	std::_tostringstream os;

	if (eLineType & LineTypeClosed)
		m_xml.AddChildElemEx(_T("polygon"));
	else
		m_xml.AddChildElemEx(_T("polyline"));

	AddFillColorAttrib(m_xml, (eLineType & LineTypeFilled) != 0);
	AddStrokeColorAttrib(m_xml, !(eLineType & LineTypeNoContour) );

	for (i=0;i<m;i++)
	{
		os<<WorldToBBx(cx)+vPx[i]<<_T(",")<<WorldToBBy(cy)-vPy[i]<<_T(" ");
	}
	m_xml.AddChildAttribEx(_T("points"),os.str().c_str());

};

void CSVGInterface::DrawPolygonStrip(const CVectorF& vCx, const CVectorF& vCy,  const CVectorF& vPx, const CVectorF& vPy, DWORD eLineType)
{
	const UINT n = __min(vCx.size(), vCy.size());
	const UINT m = __min(vPx.size(), vPy.size());
	if (n==0 || m==0)
		return;
	UINT i;

	std::_tostringstream os;
	CString strid;

	if (eLineType & LineTypeClosed)
	{
		m_xmlDefs.AddChildElemEx(_T("polygon"));
		strid.Format(_T("polg%d"),m_liCurDef);
		m_xmlDefs.AddChildAttribEx(_T("id"),strid);
		strid.Format(_T("#polg%d"),m_liCurDef);
	}
	else
	{
		m_xmlDefs.AddChildElemEx(_T("polyline"));
		strid.Format(_T("poll%d"),m_liCurDef);
		m_xmlDefs.AddChildAttribEx(_T("id"),strid);
		strid.Format(_T("#poll%d"),m_liCurDef);
	}

	// color and fill
	AddFillColorAttrib(m_xmlDefs, (eLineType & LineTypeFilled) != 0);
	AddStrokeColorAttrib(m_xmlDefs, !(eLineType & LineTypeNoContour) );

	// points...
	for (i=0;i<m;i++)
	{
		os<<vPx[i]<<_T(",")<<-vPy[i]<<_T(" ");
	}

	m_xmlDefs.AddChildAttribEx(_T("points"),os.str().c_str());
	
	// sending points
	for (i=0;i<n;i++)
	{
		m_xml.AddChildElemEx(_T("use"));
		m_xml.AddChildAttribEx(_T("x"),WorldToBBx(vCx[i]));
		m_xml.AddChildAttribEx(_T("y"),WorldToBBy(vCy[i]));
		m_xml.AddChildAttribEx(_T("xlink:href"),strid);
	}

	m_liCurDef++;
};

void CSVGInterface::PushClipWorld(double llx, double lly, double urx, double ury)
{
	CString str;
	CFileGfxInterface::PushClipWorld(llx,lly,urx,ury);

	m_xml.AddChildElemEx(_T("svg"));

	str.Format(_T("%gpx"),WorldToBBAbsx(urx-llx));
	m_xml.AddChildAttribEx(_T("width"), str);

	str.Format(_T("%gpx"),WorldToBBAbsy(ury-lly));
	m_xml.AddChildAttribEx(_T("height"), str);

	m_xml.AddChildAttribEx(_T("x"), WorldToBBx(llx));
	m_xml.AddChildAttribEx(_T("y"), WorldToBBy(ury));

	str.Format(_T("%g %g %g %g"),WorldToBBx(llx), WorldToBBy(ury), WorldToBBAbsx(urx-llx), WorldToBBAbsy(ury-lly));	
	m_xml.AddChildAttribEx(_T("viewBox"), str);
	m_xml.IntoElem();
}

void CSVGInterface::PopClipWorld()
{
	CFileGfxInterface::PopClipWorld();

	m_xml.OutOfElem();
}

void CSVGInterface::SetDash(LPCTSTR dash)
{
	int count;
	TCHAR buf[256];
	std::_tistringstream istr(dash);
	int length(0);

	m_state.m_sDash=_T("");

	count = 0;
	while ( !istr.eof())
	{
		istr>>length;
		if (count!=0)
			m_state.m_sDash+=_T(",");
		_itot(length, buf, 10);
		m_state.m_sDash+=buf;
		count++;
	}
}

void CSVGInterface::SetFont(const LOGFONT& logFont)
{
	m_state.m_font=logFont;
}

void CSVGInterface::SetLineWidth(double width)
{
	m_state.m_dLinewidth=__max(1,width);
}

void CSVGInterface::SetColor(double r, double g, double b, double a)
{
	m_state.m_cStroke.r = __max(0, __min( 1, r));
	m_state.m_cStroke.g = __max(0, __min( 1, g));
	m_state.m_cStroke.b = __max(0, __min( 1, b));
	m_state.m_cStroke.a = __max(0, __min( 1, a));
}

void CSVGInterface::SetFillColor(double r, double g, double b, double a)
{
	m_state.m_cSolidFill.r = __max(0, __min( 1, r));
	m_state.m_cSolidFill.g = __max(0, __min( 1, g));
	m_state.m_cSolidFill.b = __max(0, __min( 1, b));
	m_state.m_cSolidFill.a = __max(0, __min( 1, a));

	m_state.m_eFillType = SState::SolidFill;
}

void CSVGInterface::SetLinearGradientFill(const SPointD& point1, const SPointD& point2, const SRGBA& col1, const SRGBA& col2)
{
	m_state.m_sLinearFill.m_cStart=col1;
	m_state.m_sLinearFill.m_cEnd=col2;
	m_state.m_sLinearFill.m_pStart=point1;
	m_state.m_sLinearFill.m_pEnd=point2;
	m_state.m_eFillType = SState::LinearGradientFill;
}


void CSVGInterface::SetShadowColor(double r, double g, double b, double a)
{
	m_state.m_cShadow.r = __max(0, __min( 1, r));
	m_state.m_cShadow.g = __max(0, __min( 1, g));
	m_state.m_cShadow.b = __max(0, __min( 1, b));
	m_state.m_cShadow.a = __max(0, __min( 1, a));
}

void CSVGInterface::DrawText(double x, double y, LPCTSTR str, ETextAlignment ta, double angle)
{
	if (_tcsclen(str) == 0)
		return;

	CString temp;
	int fontSize=CreateFontSize(GetHDC(), m_state.m_font.lfHeight);

	m_xml.AddChildElemEx(_T("text"),str);
	m_xml.AddChildAttribEx(_T("font-family"),_T("Verdana"));
	m_xml.AddChildAttribEx(_T("font-size"),fontSize );
	switch(ta)
	{
	case TextAlignmentRight:
		m_xml.AddChildAttribEx(_T("text-anchor"),_T("end"));
		break;
	case TextAlignmentCenter:
		m_xml.AddChildAttribEx(_T("text-anchor"),_T("middle"));
		break;
	}

	SRGBA cTemp = m_state.m_cSolidFill;
	m_state.m_cSolidFill = m_state.m_cStroke;
	AddFillColorAttrib(m_xml,true);
	m_state.m_cSolidFill = cTemp;

	// rotating if necessary
	x=WorldToBBx(x)+sin(angle/180.0*IGFX_PI)*fontSize;
	y=WorldToBBy(y)-cos(angle/180.0*IGFX_PI)*fontSize*0.5;

	if (angle!=0)
	{
		m_xml.AddChildAttribEx(_T("x"),0);
		m_xml.AddChildAttribEx(_T("y"),0);
		temp.Format(_T("translate(%g,%g) rotate(%g)"),x,y,-angle);
		m_xml.AddChildAttribEx(_T("transform"),temp);
	}
	else
	{
		m_xml.AddChildAttribEx(_T("x"),x);
		m_xml.AddChildAttribEx(_T("y"),y);
	}
}

void CSVGInterface::DrawTextStrip(const std::vector<SPointD>& vPoints, const std::vector<std::_tstring>& vStrings, ETextAlignment ta, double angle )
{
	const UINT n = __min(vPoints.size(), vStrings.size());
	if (n==0)
		return;

	CString temp;
	int fontSize=CreateFontSize(GetHDC(), m_state.m_font.lfHeight);
	double sAngle=sin(angle/180.0*IGFX_PI)*fontSize;
	double cAngle=cos(angle/180.0*IGFX_PI)*fontSize;

	m_xml.AddChildElemEx(_T("g"));
	m_xml.AddChildAttribEx(_T("font-family"),_T("Verdana"));
	m_xml.AddChildAttribEx(_T("font-size"),fontSize );
	switch(ta)
	{
	case TextAlignmentRight:
		m_xml.AddChildAttribEx(_T("text-anchor"),_T("end"));
		break;
	case TextAlignmentCenter:
		m_xml.AddChildAttribEx(_T("text-anchor"),_T("middle"));
		break;
	}

	SRGBA cTemp = m_state.m_cSolidFill;
	m_state.m_cSolidFill = m_state.m_cStroke;
	AddFillColorAttrib(m_xml,true);

	m_xml.IntoElem();
	for (UINT i=0;i<n;i++)
	{
		m_xml.AddChildElemEx(_T("text"),vStrings[i].c_str());
		if (angle!=0)
		{
			m_xml.AddChildAttribEx(_T("x"),0);
			m_xml.AddChildAttribEx(_T("y"),0);
			temp.Format(_T("translate(%g,%g) rotate(%g)"),WorldToBBx(vPoints[i].x)+sAngle, WorldToBBy(vPoints[i].y)-cAngle/2,-angle);
			m_xml.AddChildAttribEx(_T("transform"),temp);
		}
		else
		{
			m_xml.AddChildAttribEx(_T("x"),WorldToBBx(vPoints[i].x)+sAngle);
			m_xml.AddChildAttribEx(_T("y"),WorldToBBy(vPoints[i].y)-cAngle/2);
		}
	}
	m_xml.OutOfElem();
}

void CSVGInterface::DrawSurf(const contour::TLattice<float>& tLattice, const CVectorF& vColor, DWORD dwFlag)
{
	UINT i,k;
	double r,g,b;
	UINT sideCount=tLattice.GetFaceSideSize();
	contour::TQuadGridLattice<float>::VertexContainer vPos;
	contour::TQuadGridLattice<float>::IndexContainer vIndex;
	CString strTemp, str;

	for (i=0;i<tLattice.GetFaceSize();i++)
	{
		tLattice.GetFace(i,vIndex,vPos);

		ASSERT(vIndex.size() == vPos.size());
		ASSERT(vIndex.size() == sideCount);
		r=g=b=0;
		str=_T("");
		for (k=0; k<sideCount; k++)
		{
			vPos[k].first=(float)WorldToBBx(vPos[k].first);
			vPos[k].second=(float)WorldToBBy(vPos[k].second);
			if (k==0)
				strTemp.Format(_T("M%g,%g "), vPos[k].first, vPos[k].second);
			else
				strTemp.Format(_T("L%g,%g "), vPos[k].first, vPos[k].second);

			str+=strTemp;

			r+=vColor[vIndex[k]*3];
			g+=vColor[vIndex[k]*3+1];
			b+=vColor[vIndex[k]*3+2];
		}
		str+=_T("Z");

		r/=4;
		g/=4;
		b/=4;

		SetFillColor(r,g,b);

		m_xml.AddChildElemEx(_T("path"));
		AddFillColorAttrib(m_xml,true);
		AddStrokeColorAttrib(m_xml,(dwFlag & SurfWireFrame)!=0);
		m_xml.AddChildAttribEx(_T("d"),str);
	}

}

void CSVGInterface::WriteHeader()
{
	CString str;

	// writing header to xml
	m_xml.AddElem(_T("svg"));
	m_xml.AddAttribEx(_T("xmlns"),_T("http://www.w3.org/2000/svg"));
	m_xml.AddAttribEx(_T("version"),_T("1.1"));
	str.Format(_T("%gpx"),GetBBWidth());
	m_xml.AddAttribEx(_T("width"), str);
	str.Format(_T("%gpx"),GetBBHeight());
	m_xml.AddAttribEx(_T("height"), str);
	str.Format(_T("%g %g %g %g"),GetBBLeft(), GetBBBottom(), GetBBWidth(), GetBBHeight());	
	m_xml.AddAttribEx(_T("viewBox"), str);

	// adding rendering hints
	if (GetRenderOption() & RenderOptionNoTextAntiAlias)
		m_xml.AddAttribEx(_T("text-rendering"),_T("optimizeSpeed"));

	if (GetRenderOption() & RenderOptionNoAntiAlias)
		m_xml.AddAttribEx(_T("shape-rendering"),_T("optimizeSpeed"));

	if (GetRenderOption() & RenderOptionNoBiCubicInterpolation)
		m_xml.AddAttribEx(_T("image-rendering"),_T("optimizeSpeed"));

	m_xml.AddChildElemEx(_T("title"), GetTitle());
	str=GetCreator();
	if (!str.IsEmpty())
		str+=_T(", ");
	str+=COleDateTime::GetCurrentTime().Format(_T("%d/%m/%Y, %H:%M:%S"));
	m_xml.AddChildElemEx(_T("desc"),str);

}

void CSVGInterface::DrawGridHor(double llx, double urx, double lly, double ury,double dy)
{
	CString str, strTemp;
	double y=lly;
	int count;

	m_xml.AddChildElemEx(_T("path"));
	AddStrokeColorAttrib(m_xml,true);

	// sending points
	count=0;
	y=ury;
	str.Format(_T("M %g %g "),WorldToBBx(urx), WorldToBBy(y) );
	while (y>lly)
	{
		strTemp.Format(_T("h %g m %g %g "),-WorldToBBAbsx(urx-llx), WorldToBBAbsx(urx-llx), WorldToBBAbsy(dy));
		str+=strTemp;
		count++;
		y-=dy;
		if (count%5==0)
			str+=_T("\n");
	}
	m_xml.AddChildAttribEx(_T("d"),str);
}

void CSVGInterface::DrawGridVer(double llx, double urx, double lly, double ury,double dx)
{
	CString str, strTemp;
	double x;
	int count;

	m_xml.AddChildElemEx(_T("path"));
	AddStrokeColorAttrib(m_xml,true);

	// sending points
	count=0;
	x=llx;
	str.Format(_T("M %g %g "),WorldToBBx(x), WorldToBBy(lly) );
	while (x<urx)
	{
		strTemp.Format(_T("v %g m %g %g "),-WorldToBBAbsy(ury-lly), WorldToBBAbsx(dx), WorldToBBAbsy(ury-lly));
		str+=strTemp;
		count++;
		x+=dx;
		if (count%5==0)
			str+=_T("\n");
	}
	m_xml.AddChildAttribEx(_T("d"),str);
}

void CSVGInterface::AddStrokeColorAttrib(markup::CMarkupArchive& xml, bool bContour)
{
	if (!bContour)
	{
		xml.AddChildAttribEx(_T("stroke"),_T("none"));
		return;
	}

	CString str;

	GetColorString(m_state.m_cStroke,str);

	xml.AddChildAttribEx(_T("stroke"),str);

	// dash pattern
	if (!m_state.m_sDash.IsEmpty())
		xml.AddChildAttrib(_T("stroke-dasharray"),m_state.m_sDash);

	// stroke pattern
	if (m_state.m_cStroke.a != 1)
		xml.AddChildAttribEx(_T("stroke-opacity"),m_state.m_cStroke.a);

	// stroke width
	xml.AddChildAttribEx(_T("stroke-width"),m_state.m_dLinewidth);
}

void CSVGInterface::AddFillColorAttrib(markup::CMarkupArchive& xml, bool bFilled)
{
	CString str,strid;
	if (bFilled)
	{
		switch( m_state.m_eFillType)
		{
		case SState::SolidFill:
			GetColorString(m_state.m_cSolidFill,str);
			xml.AddChildAttribEx(_T("fill"),str);
			if (m_state.m_cSolidFill.a != 1)
				xml.AddChildAttribEx(_T("fill-opacity"),(int)floor(m_state.m_cSolidFill.a*100));
			break;
		case SState::LinearGradientFill:
			// adding gradient definition
			m_xmlDefs.AddChildElemEx(_T("linearGradient"));
			strid.Format(_T("lingrad%d"), m_liCurDef);
			m_xmlDefs.AddChildAttribEx(_T("id"), strid);
			strid.Format(_T("url(#lingrad%d)"), m_liCurDef);
			// adding fill reference
			xml.AddChildAttribEx(_T("fill"),strid);

			// writing gradient...
			m_xmlDefs.AddChildAttribEx(_T("gradientUnits"),_T("userSpaceOnUse")); 
			m_xmlDefs.AddChildAttribEx(_T("x1"), WorldToBBx(m_state.m_sLinearFill.m_pStart.x));
			m_xmlDefs.AddChildAttribEx(_T("y1"), WorldToBBy(m_state.m_sLinearFill.m_pStart.y));
			m_xmlDefs.AddChildAttribEx(_T("x2"), WorldToBBx(m_state.m_sLinearFill.m_pEnd.x));
			m_xmlDefs.AddChildAttribEx(_T("y2"), WorldToBBy(m_state.m_sLinearFill.m_pEnd.y));
			m_xmlDefs.IntoElem();
				m_xmlDefs.AddChildElemEx(_T("stop"));
				m_xmlDefs.AddChildAttribEx(_T("offset"),_T("0%"));
				GetColorString(m_state.m_sLinearFill.m_cStart,str);
				m_xmlDefs.AddChildAttribEx(_T("stop-color"),str);

				if (m_state.m_sLinearFill.m_cStart.a != 1)
					xml.AddChildAttribEx(_T("stop-opacity"),(int)floor(m_state.m_sLinearFill.m_cStart.a*100));

				m_xmlDefs.AddChildElemEx(_T("stop"));
				m_xmlDefs.AddChildAttribEx(_T("offset"),_T("100%"));
				GetColorString(m_state.m_sLinearFill.m_cEnd,str);
				m_xmlDefs.AddChildAttribEx(_T("stop-color"),str);
				if (m_state.m_sLinearFill.m_cEnd.a != 1)
					xml.AddChildAttribEx(_T("stop-opacity"),(int)floor(m_state.m_sLinearFill.m_cEnd.a*100));

			m_xmlDefs.OutOfElem();

			m_liCurDef++;
			break;
		default:
			ASSERT(FALSE);
		}
	}
	else
	{
		xml.AddChildAttribEx(_T("fill"),_T("none"));
	}
}

void CSVGInterface::DrawErrorBars( const CVectorF& vXs, const CVectorF& vYs, const CVectorF& vUps, const CVectorF& vDowns, double dWidth, bool bAbsolute, bool bVertical)
{
	using namespace std;
	using namespace Gdiplus;
	UINT n;
	_tostringstream stream;

	if (bAbsolute)
	{
		if (bVertical)
			n=__min(vXs.size(), __min(vUps.size(), vDowns.size()) );
		else
			n=__min(vYs.size(), __min(vUps.size(), vDowns.size()) );
	}
	else
		n=__min(vXs.size(), __min(vYs.size(), __min(vUps.size(), vDowns.size()) ) );

	if (n==0)
		return;
	UINT i;

	m_xml.AddChildElemEx(_T("path"));
	AddStrokeColorAttrib(m_xml,true);
	float wx,wy,wUp,wDown,wLeft,wRight;

	stream.clear();
	if (!bAbsolute && bVertical)
	{
		for (i=0;i<n;i++)
		{
			wx=(REAL)WorldToBBx(vXs[i]);
			wy=(REAL)WorldToBBy(vYs[i]);
			wUp=wy+(REAL)WorldToBBAbsy(vUps[i]);
			wDown=wy-(REAL)WorldToBBAbsy(vDowns[i]);

			stream<<_T("M")<<wx-dWidth/2.0<<_T(",")<<wDown
				<<_T(" h")<<dWidth
				<<_T(" M")<<wx<<_T(",")<<wDown
				<<_T(" V")<<wUp
				<<_T(" M")<<wx-dWidth/2.0<<_T(",")<<wUp
				<<_T(" h")<<dWidth<<_T("\n");

		}
	}
	else if( !bAbsolute && ! bVertical)
	{
		for (i=0;i<n;i++)
		{
			wx=(REAL)WorldToBBx(vXs[i]);
			wy=(REAL)WorldToBBy(vYs[i]);
			wLeft=wx-(REAL)WorldToBBAbsx(vDowns[i]);
			wRight=wx+(REAL)WorldToBBAbsx(vUps[i]);

			stream<<_T("M")<<wLeft<<_T(",")<<wy-dWidth/2.0
				<<_T(" v")<<dWidth
				<<_T(" M")<<wLeft<<_T(",")<<wy
				<<_T(" H")<<wRight
				<<_T(" M")<<wRight<<_T(",")<<wy-dWidth/2.0
				<<_T(" v")<<dWidth<<_T("\n");
		}
	}
	else if (bAbsolute && !bVertical)
	{
		for (i=0;i<n;i++)
		{
			wy=(REAL)WorldToBBy(vYs[i]);
			wLeft=(REAL)WorldToBBx(vDowns[i]);
			wRight=(REAL)WorldToBBx(vUps[i]);

			stream<<_T("M")<<wLeft<<_T(",")<<wy-dWidth/2.0
				<<_T(" v")<<dWidth
				<<_T(" M")<<wLeft<<_T(",")<<wy
				<<_T(" H")<<wRight
				<<_T(" M")<<wRight<<_T(",")<<wy-dWidth/2.0
				<<_T(" v")<<dWidth<<_T("\n");
		}
	}
	else // bAbsolute && bVertical
	{
		for (i=0;i<n;i++)
		{
			wx=(REAL)WorldToBBx(vXs[i]);
			wUp=(REAL)WorldToBBy(vUps[i]);
			wDown=(REAL)WorldToBBy(vDowns[i]);

			stream<<_T("M")<<wx-dWidth/2.0<<_T(",")<<wDown
				<<_T(" h")<<dWidth
				<<_T(" M")<<wx<<_T(",")<<wDown
				<<_T(" V")<<wUp
				<<_T(" M")<<wx-dWidth/2.0<<_T(",")<<wUp
				<<_T(" h")<<dWidth<<_T("\n");

		}
	}
	m_xml.AddChildAttribEx(_T("d"),stream.str().c_str());

}

void CSVGInterface::DrawStock( double x0, double dx, const CVectorF& vHis, const CVectorF& vLows, const CVectorF& vOpens, const CVectorF& vCloses, const SRGBA& cDownColor, DWORD eStockTypeFlag)
{
	const UINT n = __min(vHis.size(), __min( vCloses.size(), __min( vOpens.size(), vLows.size()) ) );

	if (n<2)
		return;

	UINT i;
	CString str, strTemp;
	double x,wdx;
	double wx,wo,wh,wl,wc;

	m_xml.AddChildElemEx(_T("path"));
	AddStrokeColorAttrib(m_xml,true);

	x=x0;
	wdx=WorldToBBAbsx(dx/2);

	wx=WorldToBBx(x);
	wo=WorldToBBy(vOpens[0]);
	wh=WorldToBBy(vHis[0]);
	wl=WorldToBBy(vLows[0]);
	wc=WorldToBBy(vCloses[0]);

	strTemp.Format(_T("M%g,%g h%g M%g,%g V%g M%g,%g h%g "),
		wx, wo,//m
		-wdx, //h
		wx,wl, //m
		wh, //v
		wx,wc,
		wdx
		);
	str=strTemp;

	for (i=1;i<n;i++)
	{
		x+=dx;
		if (vCloses[i]<vCloses[i-1])
			continue;
		
		wx=WorldToBBx(x);
		wo=WorldToBBy(vOpens[i]);
		wh=WorldToBBy(vHis[i]);
		wl=WorldToBBy(vLows[i]);
		wc=WorldToBBy(vCloses[i]);
		
		strTemp.Format(_T("M%g,%g h%g M%g,%g V%g M%g,%g h%g "),
			wx, wo,//m
			-wdx, //h
			wx,wl, //m
			wh, //v
			wx,wc,
			wdx
			);
		str+=strTemp;		
	}

	m_xml.AddChildAttribEx(_T("d"),str);

	////////////////////////////////////////////////:
	// down...
	m_xml.AddChildElemEx(_T("path"));
	AddStrokeColorAttrib(m_xml,true);
	GetColorString(cDownColor,str);
	m_xml.AddChildAttribEx(_T("stroke"),str);
	x=x0;
	str=_T("");
	for (i=1;i<n;i++)
	{
		x+=dx;
		if (vCloses[i]>=vCloses[i-1])
			continue;
		
		wx=WorldToBBx(x);
		wo=WorldToBBy(vOpens[i]);
		wh=WorldToBBy(vHis[i]);
		wl=WorldToBBy(vLows[i]);
		wc=WorldToBBy(vCloses[i]);
		
		strTemp.Format(_T("M%g,%g h%g M%g,%g V%g M%g,%g h%g "),
			wx, wo,//m
			-wdx, //h
			wx,wl, //m
			wh, //v
			wx,wc,
			wdx
			);
		str+=strTemp;		
	}
	m_xml.AddChildAttribEx(_T("d"),str);
}

void CSVGInterface::DrawVectorField(const contour::TLattice<float>& tLattice, const CVectorF& vVx, const CVectorF& vVy, float fScale, DWORD dwFlags)
{
	using namespace contour;
	using namespace std;
	static double tol=numeric_limits<double>::denorm_min()*numeric_limits<double>::denorm_min();

	const UINT n = __min(tLattice.GetVertexSize(), __min( vVx.size(), vVy.size()));
	if (n==0)
		return;
	double dx,dy,norm;
	CString str,strTemp;

	m_xml.AddChildElemEx(_T("g"));
	AddStrokeColorAttrib(m_xml,true);
	if (!(dwFlags & VectorFieldNoStartCap))
	{
	}
	if (!(dwFlags & VectorFieldNoEndCap))
	{
		str.Format(_T("TriangleCap%d"), m_liCurDef);
		// adding to definition
		m_xmlDefs.AddChildElemEx(_T("marker"));
		AddStrokeColorAttrib(m_xmlDefs,true);
		m_xmlDefs.AddChildAttribEx(_T("id"),str);
		m_xmlDefs.AddChildAttribEx(_T("viewBox"),_T("0 0 10 10"));
		m_xmlDefs.AddChildAttribEx(_T("refX"),_T("0"));
		m_xmlDefs.AddChildAttribEx(_T("refY"),_T("5"));
		m_xmlDefs.AddChildAttribEx(_T("markerUnits"),_T("strokeWidth"));
		m_xmlDefs.AddChildAttribEx(_T("markerWidth"),_T("6"));
		m_xmlDefs.AddChildAttribEx(_T("markerHeight"),_T("4"));
		m_xmlDefs.AddChildAttribEx(_T("orient"),_T("auto"));
		m_xmlDefs.IntoElem();
			m_xmlDefs.AddChildElemEx(_T("path"));
			GetColorString(m_state.m_cStroke,str);
			m_xmlDefs.AddChildAttribEx(_T("fill"),str);
			if (m_state.m_cStroke.a != 1)
				m_xmlDefs.AddChildAttribEx(_T("fill-opacity"),(int)floor(m_state.m_cStroke.a*100));
			m_xmlDefs.AddChildAttribEx(_T("d"),_T("M 0 0 L 10 5 L 0 10 z"));
		m_xmlDefs.OutOfElem();
		// adding arrow
		str.Format(_T("url(#TriangleCap%d)"), m_liCurDef);
		m_xml.AddChildAttribEx(_T("marker-end"),str);

		m_liCurDef++;
	}
	m_xml.IntoElem();	

	for (UINT i=0;i<n;i++)
	{
		norm=vVx[i]*vVx[i] + vVy[i]*vVy[i];
		if ( norm < tol )
			continue;

		if (dwFlags & VectorFieldConstantLength)
		{
			norm=sqrt(norm);
			dx=vVx[i]*fScale/norm*20;
			dy=vVy[i]*fScale/norm*20;
		}
		else
		{
			dx=WorldToBBAbsx(vVx[i]*fScale);
			dy=WorldToBBAbsx(vVy[i]*fScale);
		}

		strTemp.Format(_T("M%g,%g l%g,%g "),
			WorldToBBx( tLattice.GetXVertex(i)),
			WorldToBBy( tLattice.GetYVertex(i)),
			dx, dy);

		m_xml.AddChildElemEx(_T("path"));		
		m_xml.AddChildAttribEx(_T("d"), strTemp);	
	}
	
	m_xml.OutOfElem();
}

LPCTSTR CSVGInterface::GetFileExt() const				
{
	switch(m_eOutputType)
	{
	case OutputSVGZ:
		return _T("svgz");
	default:
		return _T("svg");
	}
};

}
